<template>
  <div class="container">
    <div class="left">
      <div ref="box" class="box"></div>

      <div class="btn">
        <button @click="create">创建批注</button>
        <button @click="save">保存批注</button>
        <button @click="cancel">取消批注</button>
      </div>
    </div>

    <div ref="right" class="right">
      <h5>截图的图片</h5>
      <div class="content">
        <div v-for="(v, i) in list" :key="i" class="item" @click="view(v)">
          <img :src="v.originBase64" />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { Annotation, html2canvas, shadowDom } from './index'
import doc from './docDemo'

export default {
  data() {
    return {
      ann: null,
      list: [],
      shadowDom: null
    }
  },
  computed: {
    el() {
      return this.$refs.box
    }
  },
  watch: {},
  created() {
    if (!window.customElements.get('my-shadow-diff')) {
      window.customElements.define('my-shadow-diff', shadowDom)
    }
  },
  mounted() {
    const ele = document.createElement('my-shadow-diff')
    this.el.appendChild(ele)
    ele.setStyle(`
		.container {
    		overflow: auto;
		    height: 100%
      }
      .container .dom-show{
		    visibility: unset;
		  }
		  .container .dom-hide{
		    visibility: hidden;
		  }
		  .container .remove{
		    display: none;
		  }
		`)
    ele.appendWrapper(doc)
    // ele.appendCanvas('<h1>world</h1>')
    this.shadowDom = ele
    this.setDomObserver()
  },
  methods: {
    async create(annotations = [], readonly = false) {
      const box = this.el
      const container = this.shadowDom.getContainer()
      const shadowDom = this.shadowDom
      this.ann = await new Annotation(box, {
        annotations: annotations,
        toolConfigs: {
          hideSaveGroup: true,
          hideHistoryGroup: false, // 为false 批注可以删除
          showTools: !readonly
        },
        async setBgImg() {
          const canvas = await html2canvas(container, {
            backgroundColor: '#fff',
            useCORS: true,
            allowTaint: true,
            width: container.offsetWidth,
            height: container.offsetHeight,
            scale: 1, // 解决mac分辨率高导致截图和批注不对齐的问题
            dpi: window.devicePixelRatio * 300,
            ignoreElements: e => {
              // 性能优化，只加载相关节点
              const bol =
                (e?.contains(shadowDom) || container.parentNode?.contains(e)) &&
                !['link', 'script', 'noscript', 'meta', 'head', 'iframe', 'svg', 'symbol', 'path'].includes(
                  e.tagName?.toLocaleLowerCase() || e.localName?.toLocaleLowerCase() || ''
                )

              if (container.parentNode?.contains(e) && e.className?.includes('wrapper')) {
                return true
              }
              return !bol
            }
          })

          // console.log('22', canvas.toDataURL('image/png'))
          // this.$refs.right.appendChild(canvas)
          // debugger
          return canvas.toDataURL('image/png')
        }
      })
      readonly ? this.ann._restore(annotations) : this.ann.start()

      // 如果截图非常卡，需要使用这个方法
      await this.buildCanvas()
      this.shadowDom.toggleShowWrapper(false)
    },

    // 这里是性能优化，先获取截图的背景图  当截图的时候 不再重新获取截图了
    async buildCanvas() {
      const warpper = this.shadowDom.getWrapper()
      const parentBox = this.shadowDom

      return new Promise(resolve => {
        setTimeout(async () => {
          // 先将不可见节点都销毁，吧第一个可见的节点用margin撑起来   生成底图完成后再还原
          const scrollTop = warpper.parentElement.scrollTop
          const ele = warpper.getElementsByClassName('dom-show')
          if (ele[0]) ele[0].style.marginTop = ele[0].offsetTop - 16 + 'px' // 16px是有个内边距
          const hideEle = warpper.getElementsByClassName('dom-hide')
          Array.from(hideEle).forEach(e => e.classList.add('remove'))

          console.time('创建底图耗时')
          try {
            const canvasEle = await html2canvas(warpper, {
              backgroundColor: '#fff',
              useCORS: true,
              allowTaint: true,
              scale: 1,
              dpi: window.devicePixelRatio * 300,
              ignoreElements: e => {
                // 性能优化，只加载相关节点
                let bol =
                  (e?.contains(parentBox) || warpper.parentElement.parentNode?.contains(e)) &&
                  !['link', 'script', 'noscript', 'meta', 'head', 'iframe', 'svg', 'symbol', 'path'].includes(
                    e.tagName?.toLocaleLowerCase() || e.localName?.toLocaleLowerCase() || ''
                  )
                if (bol) {
                  if (
                    warpper.parentElement.parentNode?.contains(e) &&
                    e.classList.values().some(e => e === 'dom-hide')
                  ) {
                    bol = false
                  }
                }
                return !bol
              }
            })

            canvasEle.setAttribute('class', 'canvas')

            // 删除无用的canvas
            Array.from(warpper.parentElement.children).forEach(e => {
              if (e.tagName === 'CANVAS') warpper.parentElement.removeChild(e)
            })
            // canvasEle.style.marginTop = warpper.parentElement.scrollTop + 'px'
            // canvasEle.style.marginLeft = warpper.parentElement.scrollLeft + 'px'
            warpper.parentElement.appendChild(canvasEle)

            console.timeEnd('创建底图耗时')
            console.log('底图生成完毕！!!!!!!!!!')
          } finally {
            // 还原
            Array.from(hideEle).forEach(e => e.classList.remove('remove'))
            ele[0].style.marginTop = 0
            warpper.parentElement.scrollTop = scrollTop
            resolve()
          }
        }, 100) // 这个需要设置100， 设置30会有问题，页面编辑时滚动条高度定位不及时，导致生成底图错误
      })
    },

    // 给可视区/非可视区写入show和hide的class
    setDomObserver(dom) {
      const observer = this.buildObserver()
      Array.from(this.shadowDom.getWrapper().querySelectorAll('div')).map(e =>
        Array.from(e.children).forEach(m => observer.observe(m))
      )
    },

    // 可视区监听器
    buildObserver() {
      // 回调函数，在目标元素与根元素的交叉状态发生变化时调用
      const callback = (entries, observer) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            entry.target.classList.add('dom-show')
            entry.target.classList.remove('dom-hide')
          } else {
            entry.target.classList.add('dom-hide')
            entry.target.classList.remove('dom-show')
          }
        })
      }
      const observer = new IntersectionObserver(callback, {
        root: null, // 默认为浏览器视窗
        rootMargin: '0px',
        threshold: 0.1 // 交叉比例阈值，可以是一个数字或数字数组，表示在不同的交叉比例下触发回调
      })
      return observer
    },

    // 将截图定位到页面中的相对位置
    locationScroll([top, left]) {
      this.shadowDom.getContainer().scrollTop = top
      this.shadowDom.getContainer().scrollLeft = left
    },

    async save() {
      const container = this.shadowDom.getContainer()
      const annotate = await this.ann.getSaveData({
        boundaryWidth: container.offsetWidth,
        boundaryHeight: container.offsetHeight
      })

      annotate.xdata.scroll = [container.scrollTop, container.scrollLeft]

      this.list.push(annotate)
      this.cancel()
    },

    cancel() {
      if (this.ann?.annotationHelp?.editor) {
        this.ann.destroy() // 销毁标注
        this.ann = null
      }
      this.shadowDom.toggleShowWrapper(true) // 显示内容区
      // 删除截图的canvas
      const warpper = this.shadowDom.getWrapper()
      Array.from(warpper.parentElement.children).forEach(e => {
        if (e.tagName === 'CANVAS') warpper.parentElement.removeChild(e)
      })
    },

    view(item) {
      this.cancel()
      this.locationScroll(item.xdata.scroll)
      this.create(item.xdata.annotations, true) // 如果是修改就传false
    }
  }
}
</script>

<style lang="less" scoped>
.container {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  .left {
    width: 50%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .box {
      position: relative;
      border: 1px solid red;
      width: 80%;
      height: 500px;
      margin: 0 auto;
      text-align: center;
      overflow: hidden;
    }
    .btn {
      margin-top: 20px;
      width: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      column-gap: 16px;
    }
  }
  .right {
    width: 50%;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    .content {
      display: flex;
      justify-content: flex-start;
      flex-wrap: wrap;
      align-items: flex-start;
    }
  }
}
</style>
